還沒看過悲重寫的上篇....可以先去看看。
OK,今天帶來的是倖存的下篇。今天會正式進到程式碼的部份。不多說,直接開始吧!
最後會附上完整程式碼,可以往下複製使用 (由於語法屬於舊式,不是很建議大伙深入探討程式)。
安裝 truffle (乙太坊開發框架)。
npm install -g truffle
建立專案目錄
mkdir pet-shop-tutorial
cd pet-shop-tutorial
初始化範例程式
truffle unbox pet-shop
在contract
目錄下,建立Adoption.sol
檔案,並輸入以下程式:
pragma solidity ^0.5.0;
contract Adoption {
address[16] public adopters;
// Adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
// Retrieving the adopters
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
}
truffle compile
會得到類似下面輸出。
Compiling your contracts...
===========================
> Compiling ./contracts/Adoption.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /Users/cruzmolina/Code/truffle-projects/metacoin/build/contracts
> Compiled successfully using:
- solc: 0.5.0+commit.1d4f565a.Emscripten.clang
接著在migrations/
資料夾下新增2_deploy_contracts.js
檔案,並寫入下面內容:
var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};
然後下命令truffle migrate
,會得到類似結果:
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x3b558e9cdf1231d8ffb3445cb2f9fb01de9d0363e0b97a17f9517da318c2e5af
> Blocks: 0 Seconds: 0
> contract address: 0x5ccb4dc04600cffA8a67197d5b644ae71856aEE4
> account: 0x8d9606F90B6CA5D856A9f0867a82a645e2DfFf37
> balance: 99.99430184
> gas used: 284908
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00569816 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00569816 ETH
2_deploy_contracts.js
=====================
Deploying 'Adoption'
.............................
.............................
現在,我們有了區塊鏈網路,有了區塊鏈錢包,有了合約,再來是需要與合約互動的方式。編輯src/js/app.js
,改成以下內容:
App = {
web3Provider: null,
contracts: {},
init: async function() {
// Load pets.
$.getJSON('../pets.json', function(data) {
var petsRow = $('#petsRow');
var petTemplate = $('#petTemplate');
for (i = 0; i < data.length; i ++) {
petTemplate.find('.panel-title').text(data[i].name);
petTemplate.find('img').attr('src', data[i].picture);
petTemplate.find('.pet-breed').text(data[i].breed);
petTemplate.find('.pet-age').text(data[i].age);
petTemplate.find('.pet-location').text(data[i].location);
petTemplate.find('.btn-adopt').attr('data-id', data[i].id);
petsRow.append(petTemplate.html());
}
});
return await App.initWeb3();
},
initWeb3: async function() {
// Modern dapp browsers...
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.enable();
} catch (error) {
// User denied account access...
console.error("User denied account access");
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider('http://eos.cloudlab.mywire.org:7545');
}
web3 = new Web3(App.web3Provider);
return App.initContract();
},
initContract: function() {
$.getJSON('Adoption.json', function(data) {
// Get the necessary contract artifact file and instantiate it with truffle-contract
var AdoptionArtifact = data;
App.contracts.Adoption = TruffleContract(AdoptionArtifact);
// Set the provider for our contract
App.contracts.Adoption.setProvider(App.web3Provider);
// Use our contract to retrieve and mark the adopted pets
return App.markAdopted();
});
return App.bindEvents();
},
bindEvents: function() {
$(document).on('click', '.btn-adopt', App.handleAdopt);
},
markAdopted: function(adopters, account) {
var adoptionInstance;
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
return adoptionInstance.getAdopters.call();
}).then(function(adopters) {
for (i = 0; i < adopters.length; i++) {
if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
$('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
}
}
}).catch(function(err) {
console.log(err.message);
});
},
handleAdopt: function(event) {
event.preventDefault();
var petId = parseInt($(event.target).data('id'));
var adoptionInstance;
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
// Execute adopt as a transaction by sending account
return adoptionInstance.adopt(petId, {from: account});
}).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});
}
};
$(function() {
$(window).load(function() {
App.init();
});
});
來運行看看npm run dev
。應該會自動開啟瀏覽器瀏覽http://localhost:3000
,並且MetaMask會要求登入。
※ 你可能要先執行npm install
允許後每隻狗狗就可以進行領養囉(Adopt)。
DApp的意思是分散式應用程式,就簡單理解為區塊鏈上智能合約的應用程式就好。目前大多數DApp還是以網頁形式與使用者互動。這之中還有一些技術細節,已有有需要在提。
那麼關於這次範例的DApp的組成:
組件 | 軟體 | 備註說明 |
---|---|---|
區塊鏈底層 | Ganache | 乙太坊區塊鏈網路 |
區塊鏈虛擬機 | Ganache | 乙太坊虛擬機(EVM)。提供智能合約執行的環境 |
智能合約 | Soilidty | 本次合約程式內容 |
瀏覽器畫面 | HTML5+Web3.js | Web3.js是使瀏覽器與乙太坊錢包溝通的橋樑 |
※ 我正式接觸乙太坊是在EOS之後,並且也不算太深入,不過許多概念可以相通。未來會以EOS來說。